Shader "RedSaw/VolumetricCloud"{
    
    Properties{
        _BaseColor("Base Color", Color) = (1, 1, 1, 1)
    }
    SubShader{
        Tags{
            "RenderPipeline" = "UniversalRenderPipeline"
        }
        pass{

            Cull Off
            ZTest Always
            ZWrite Off
            
            HLSLPROGRAM

                #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
                #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/SpaceTransforms.hlsl"
                #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
                #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
                #pragma vertex Vertex
                #pragma fragment Pixel

                Texture2D _SourceTex;
                SamplerState sampler_SourceTex;
                sampler2D _weatherMap;
                sampler2D _maskNoise;
                sampler3D _NoiseTex;
                sampler3D _detailNoiseTex;
                
                float _densityOffset;
                float _cloudHeightWeight;
                float _lightAbsorptionTowardSun;
                half4 _lightColor;
                half4 _cloudColor;

                half4 _darknessThreshold;
                half4 _CloudMidColor;
                half4 _CloudDeepColor;
                float _CloudMidColorOffset;
                float _CloudDeepColorOffset;
                
                
                float4 _phaseParams;
                float3 _CloudMaxDis;
                float3 _CloudMinDis;
                float4 _shapeNoiseWeights;
                float _shapeTiling ;
                float _detailTiling ;
                float4 _xy_Speed_zw_Warp;
                float3 GetWorldPosition(float3 positionHCS)
                {
                    /* get world space position from clip position */

                    float2 UV = positionHCS.xy / _ScaledScreenParams.xy;
                    #if UNITY_REVERSED_Z
                    real depth = SampleSceneDepth(UV);
                    #else
                    real depth = lerp(UNITY_NEAR_CLIP_VALUE, 1, SampleSceneDepth(UV));
                    #endif
                    return ComputeWorldSpacePosition(UV, depth, UNITY_MATRIX_I_VP);
                }
                float2 rayBoxDst(float3 boundsMin, float3 boundsMax, float3 rayOrigin, float3 rayDir)
                {
                    /*  通过boundsMin和boundsMax锚定一个长方体包围盒
                        从rayOrigin朝rayDir发射一条射线，计算出射线到包围盒的距离
                        about ray box algorithm 
                        https://jcgt.org/published/0007/03/04/ */

                    float3 t0 = (boundsMin - rayOrigin) / rayDir;
                    float3 t1 = (boundsMax - rayOrigin) / rayDir;
                    float3 tmin = min(t0, t1);
                    float3 tmax = max(t0, t1);

                    float dstA = max(max(tmin.x, tmin.y), tmin.z);
                    float dstB = min(tmax.x, min(tmax.y, tmax.z));

                    float dstToBox = max(0, dstA);
                    float dstInsideBox = max(0, dstB - dstToBox);
                    return float2(dstToBox, dstInsideBox);
                }

                //瑞利散射
                float hg(float a, float g)
                {
                    float g2 = g * g;
                    return(1 - g2) / (4 * 3.1415 * pow(1 + g2 - 2 * g * (a), 1.5));
                }
                float phase(float a)
                {
                    float blend = 0.5;
                    float hgBlend = hg(a, _phaseParams.x) * (1 - blend) + hg(a, -_phaseParams.y) * blend;
                    return _phaseParams.z + hgBlend * _phaseParams.w;
                }
               
                float remap(float original_value, float original_min, float original_max, float new_min, float new_max)
                {
                    return new_min + (((original_value - original_min) / (original_max - original_min)) * (new_max - new_min));
                }
                
                
                

                //获取云类型密度
                float GetCloudTypeDensity(float heightFraction, float cloud_min, float cloud_max, float feather)
                {
                    //云的底部羽化需要弱一些，所以乘0.5
                    return saturate(remap(heightFraction, cloud_min, cloud_min + feather * 0.5, 0, 1))
                    * saturate(remap(heightFraction, cloud_max - feather, cloud_max, 1, 0));
                }
                float3 GetLocPos(float3 pos)
                {
                	// sampling as 4D texture
                    float3 size = _CloudMaxDis - _CloudMinDis;
                	pos = ((pos -_CloudMinDis)/(size)+float3(1,1+abs(sin(_Time.y*0.1)*sin(_Time.y*0.05)*cos(_Time.y*0.08)),1))*0.5;
                	return pos;
                }

                float SampleDensity(float3 rayPos) 
                {
                    //前期处理
                    float3 boundsCentre = (_CloudMaxDis + _CloudMinDis) * 0.5;
                    float3 size = _CloudMaxDis - _CloudMinDis;
                    ////重新计算uv坐标，通过包围盒和当前射线追踪的位置与中心点的偏离来进行采样
                    float2 uv = (size.xz * 0.5f + (rayPos.xz - boundsCentre.xz) ) /max(size.x,size.z);


                    
                    float speedShape = _Time.y * _xy_Speed_zw_Warp.x;//获取噪音速度变化
                    float speedDetail = _Time.y * _xy_Speed_zw_Warp.y;//获取细节噪音速度变化
                    float3 offset=frac(rayPos*_shapeTiling);
                    float3 uvwShape = offset ;//uvw速度变化效果//+ float3(speedShape,speedShape*0.2,0)
                    float3 uvwDetail = rayPos * _detailTiling + float3(speedDetail,speedDetail * 0.2,0);//uvw速度变化效果

                    //遮罩噪声 扭曲云，让它有点变化 遮罩噪音采样
                    float4 maskNoise = tex2D(_maskNoise,uv + float2(speedShape * 0.5,0));//
                    //基础噪声采样
                    float3 shapePose=min(float3(0.95,0.95,0.95),max(float3(0.05,0.05,0.05),frac(GetLocPos(rayPos)*_shapeTiling)));
                    shapePose+=float3(speedShape,speedShape*0.2,0);
                    float4 shapeNoise = tex3Dlod(_NoiseTex, float4(shapePose,0));
                    //+ (maskNoise.r * _xy_Speed_zw_Warp.z)
                    //细节噪声采样
                    //float4 detailNoise = tex3Dlod(_detailNoiseTex, float4(uvwDetail + (shapeNoise.r * _xy_Speed_zw_Warp.w * 0.1), 0));//细节噪音采样

                    float4 weatherMap = tex2D(_weatherMap, uv+ float2(speedShape * 0.4, 0));//
                    
                    
                    float heightPercent = (rayPos.y- _CloudMinDis.y ) / size.y;//计算一个梯度值
                    
                    half gMin = remap(weatherMap.r, 0, 1, 0.1, 0.6);
                    half gMax = remap(weatherMap.r, 0, 1, gMin, 0.9);
                    // 两个梯度用于控制云层的高度，添加_CloudHeight变量用于控制高度
                    // 通过线性插值控制第一个梯度还是第二个梯度
                    // heightGradientMin表示梯度最小的情况，即没有梯度；heightGradientMax表示梯度最大的情况，表示梯度的情况最大，即梯度最大。
                    half heightGradientMin = saturate(remap(heightPercent, 0.0, gMin, 0, 1))
                                            * saturate(remap(heightPercent, 1, gMax, 0, 1));

                    
                    //float finalMaxShape=lerp(weatherMap.r,shapeNoise.r,_shapeNoiseWeights.w);
                    half heightGradientMax = saturate(remap(heightPercent, 0.0, weatherMap.r, 1, 0))
                                            * saturate(remap(heightPercent, 0.0, gMin, 0, 1));
                    half heightGradientResult = saturate(lerp(heightGradientMin, heightGradientMax,_cloudHeightWeight));
                    #if 1
                    //边缘软化,让边缘变换不是突变
                    const float containerEdgeFadeDst = min(size.x,size.z);//边缘衰减常数
                    float dstFromEdgeX = min(containerEdgeFadeDst, min(rayPos.x - _CloudMinDis.x, _CloudMaxDis.x - rayPos.x));//对边缘进行衰减，根据边框的边缘进行判断，如果达到边缘则进行取值，边框内不能取值过大
                    float dstFromEdgeZ = min(containerEdgeFadeDst, min(rayPos.z - _CloudMinDis.z, _CloudMaxDis.z - rayPos.z));//对边缘进行衰减，根据边框的边缘进行判断，如果达到边缘则进行取值，边框内不能取值过大
                    float edgeWeight = min(dstFromEdgeZ, dstFromEdgeX) / containerEdgeFadeDst;//获取最低边框位置
                    #endif
                    
                    heightGradientResult*=edgeWeight;
                    
                    float shapeFBM = dot(shapeNoise,_shapeNoiseWeights)*heightGradientResult;
                    return shapeFBM+_densityOffset*0.01;
                    //float baseShapeDensity = (heightGradientResult)*(weatherMap.r)+_densityOffset;
                    #if 0
                    if(baseShapeDensity>0)
                    {
                        float detailFBM = pow(shapeFBM.r, _shapeNoiseWeights.x);
                        float oneMinusShape = 1 - baseShapeDensity;
                        half sampleDensityResult = saturate(baseShapeDensity  - detailFBM * oneMinusShape * _shapeNoiseWeights.z ); 
                        
                        return sampleDensityResult;
                    }
                    #else
                        //return baseShapeDensity;
                    #endif
                    
                    return 0;

                    /*
                    //归一化噪声影响权重,防止超出范围，可能会有...
                    float4 normalizedShapeWeights = _shapeNoiseWeights / dot(_shapeNoiseWeights,float4(1,1,1,1));
                    //对噪音进行整体修正,简单塑性
                    float shapeFBM = dot(shapeNoise + shapeNoise * detailNoise,normalizedShapeWeights);
                    
                    //【天气系列计算】采样天气图
                    float4 weatherMap = tex2D(_weatherMap, uv+ float2(speedShape * 0.4, 0));//
                    
                    //高度变换 可以使云不至于是一个圆柱https://blog.csdn.net/whl0071/article/details/127986949
                    //就是根据高度，随机调整密度
                    float heightPercent = (rayPos.y - _CloudMinDis.y)/size.y;//得到高度雾的梯度值，摄像机位置的y与包围盒的y进行相减，得到在包围盒的比例值

                    float gMin = remap(weatherMap.r,0,1,0,0.3);
                    float gMax = remap(weatherMap.r, 0, 1, gMin, 0.9);
                    float heightGradient = saturate(remap(heightPercent, 0.0, gMin, 0, 1))* saturate(remap(heightPercent, 1, gMax, 0, 1));//重新映射，得到最终的体积云效果
                    
                    //
                    //float heightGradientt = saturate(remap(heightPercent, 0.0, weatherMap.r, 1, 0));
                    
                    
                    
                    return heightGradient*(shapeFBMs+_densityOffset);
                    //云最低和最高层
                    
                    
                    //【进行合并】
                    float baseShapeDensity = shapeFBM * heightGradient + _densityOffset;//对密度进行整体调整
        
                    return baseShapeDensity;
                    */
                    
                }
                float3 lightmarch(float3 position ,float dstTravelled)
                {
                    Light mainLight=GetMainLight();
                   float3 dirToLight = mainLight.direction;
                   //灯光方向与边界框求交，超出部分不计算
                   float dstInsideBox = rayBoxDst(_CloudMinDis, _CloudMaxDis, position, 1 / dirToLight).y;
                   float stepSize = dstInsideBox / 10;
                   float totalDensity = 0;
                
                  for (int step = 0; step < 8; step++) //灯光步进次数
                  { 
                     position += dirToLight * stepSize; //向灯光步进
                     totalDensity += max(0, SampleDensity(position) * stepSize); //步进的时候采样噪音累计受灯光影响密度
                  }
                    
                  float transmittance = exp(-totalDensity * _lightAbsorptionTowardSun);
                  //将重亮到暗映射为 3段颜色 ,亮->灯光颜色 中->ColorA 暗->ColorB
                  float3 cloudColor = lerp(_CloudMidColor, mainLight.color.rgb, saturate(transmittance * _CloudMidColorOffset));
                  cloudColor = lerp(_CloudDeepColor, cloudColor, saturate(pow(transmittance * _CloudDeepColorOffset, 3)));
                  return _darknessThreshold + transmittance * (1 - _darknessThreshold) * cloudColor;
                }
                struct vertexInput{
                    float4 vertex: POSITION;
                    float2 uv: TEXCOORD0;
                };
                struct vertexOutput{
                    float4 pos: SV_POSITION;
                    float2 uv: TEXCOORD0;
                };

                vertexOutput Vertex(vertexInput v){

                    vertexOutput o;
                    o.pos = TransformObjectToHClip(v.vertex);
                    o.uv = v.uv;
                    return o;
                }
                half4 Pixel(vertexOutput IN): SV_TARGET
                {
                     // 采样主纹理
                    half4 albedo = _SourceTex.Sample(sampler_SourceTex, IN.uv);

                    // 重建世界坐标
                    float3 worldPosition = GetWorldPosition(IN.pos);
                    //half4 FragColor;
                    //if(worldPosition.x<10&&worldPosition.x>0
                    //    &&worldPosition.y<10&&worldPosition.y>0
                    //    &&worldPosition.z<10&&worldPosition.z>0)
                    //{
                    //  
                    //    float4 shapeNoise = tex3Dlod(_NoiseTex, float4(worldPosition,0));
                    //    FragColor = half4(shapeNoise);
                    //}
                    //else
                    //{
                    //    FragColor=half4(1,1,1,1);
                    //}
                    //
                    //return FragColor;

                    
                    float3 rayPosition = _WorldSpaceCameraPos.xyz;
                    float3 worldViewVector = worldPosition - rayPosition;
                    float3 rayDir = normalize(worldViewVector);


                    // 碰撞体积计算
                    float2 rayBoxInfo = rayBoxDst(
                        _CloudMinDis, _CloudMaxDis,
                        rayPosition, rayDir);
                    float dstToBox = rayBoxInfo.x;
                    float dstInsideBox = rayBoxInfo.y;
                    float dstToOpaque = length(worldViewVector);
                    float dstLimit = min(dstToOpaque - dstToBox, dstInsideBox);

                    int stepCount = 32;                                     // 总步数
                    float3 entryPoint = rayPosition + rayDir * dstToBox;    // 采样起点
                    float stepSize = dstInsideBox / stepCount;              // 步长
                    float totalDensity = 0;                                 // 浓度积分
                    float dstTravelled = 0;					// 已经走过的距离，与dstLimit所比较可以中断当前的采样
                    float sumDensity = 1;
                    float3 lightEnergy = 0;
                    //【光线散射】
                    float cosAngle = dot(worldViewVector, GetMainLight().direction.xyz);
                    float3 phaseVal = phase(cosAngle);
                   
                    //float sumDensity = 1;
                    [unroll(88)]
                    for(int i = 0; i < stepCount; i ++)
                    {
                       
                        if(dstTravelled < dstLimit)
                        {
                            rayPosition = entryPoint + (rayDir * dstTravelled);
                            float density=SampleDensity(rayPosition);
                            if(density>0)
                            {
                                //
                                float3 lightTransmittance = lightmarch(rayPosition, dstTravelled);//光线传播
                                lightEnergy += density*stepSize*sumDensity*lightTransmittance;
                                //// larger density, smaller transmittance
                                sumDensity *= exp(-density*stepSize);
                               
                                if(sumDensity < 0.01f )
                                {
                                    break;
                                }
                            }
                           
	
                        }
                       
                        dstTravelled += stepSize;
                    }
                    //return half4(lightEnergy,1.0f);     
                    half4 cloud=half4(lightEnergy,sumDensity);
                    half4 final_color = lerp(half4(cloud.rgb,1.0),albedo,cloud.a);//根据云朵判断颜色选择
                    
                    return final_color;
                }
            ENDHLSL
        }
    }
}